﻿using Application.Service;
using Infrastructure.Extension;
using Infrastructure.Utils;
using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json;
using Polly;
using Quartz;
using System.Diagnostics;
using Neoit.Utils.TemplateParser;
using Microsoft.Extensions.Logging.Abstractions;
using System;
using System.Xml.Linq;
using Newtonsoft.Json.Serialization;
using System.Threading.Tasks;
using System.Data;

namespace Application.Job
{
    public class TaskJob : IJob
    {
        private readonly JobManagerDbContext _dbContext;
        private readonly HttpClient _httpClient;
        private readonly Random _random = new Random();
        private bool _isRunOnce = false;
        private IJobService _jobService { get; set; }
        public TaskJob(JobManagerDbContext dbContext, IHttpClientFactory httpClientFactory, IJobService service)
        {
            _dbContext = dbContext;
            _httpClient = httpClientFactory.CreateClient();
            _jobService = service;
        }

        private JsonSerializerSettings _camelCaseIndented = new JsonSerializerSettings
        {
            ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
            DateFormatString = "yyyy/MM/dd HH:mm:ss",
            ContractResolver = new CamelCasePropertyNamesContractResolver(),
            Formatting = Formatting.Indented
        };

        public async Task Execute(IJobExecutionContext context)
        {
            var now = DateTime.Now;
            var isSuccess = false;
            var result = string.Empty;
            var response = string.Empty;
            var dataMap = context.Trigger.JobDataMap;
            _isRunOnce = dataMap.GetBoolean("IsRunOnce");
            var isMainTask = dataMap.GetBoolean("IsMainTask");//是否由“运行一次”，和cron触发的任务；否则为任务的前置任务或后置任务
            var runId = dataMap.GetLong("RunId");
            long dataId;
            long mainTaskId = dataMap.GetLong("MainTaskId");
            if (_isRunOnce)
            {
                dataId = dataMap.GetLong("DataId");
            }
            else
            {
                dataId = Convert.ToInt64(context.JobDetail.Key.Name);
                mainTaskId = dataId;
            }
            var mainData = await _dbContext.JobTask.FirstOrDefaultAsync(s => s.Id == mainTaskId);
            var runData = await _dbContext.JobTaskRun.FirstOrDefaultAsync(s => s.Id == runId);
            var data = await _dbContext.JobTask.FirstOrDefaultAsync(s => s.Id == dataId);
            var error = string.Empty;

            #region 添加执行记录
            var listSteps = new List<JobRunStep>();
            if (runData==null)
            {
                if (mainData.PreJobId != null)
                {
                    var _data = await _dbContext.JobTask.FirstOrDefaultAsync(s => s.Id == mainData.PreJobId);
                    if (_data==null) error = $"ID为{mainData.PreJobId}的任务已不存在";
                    listSteps.Add(new JobRunStep
                    {
                        TaskId = mainData.PreJobId,
                        TaskTitle= _data?.Title,
                        Content= _data?.Content,
                        Status = JobRunStepStatus.pending,
                        Result= _data==null? "[任务不存在]" : ""
                    });
                }
                listSteps.Add(new JobRunStep
                {
                    TaskId = mainData.Id,
                    TaskTitle = mainData.Title,
                    Content = mainData.Content,
                    Status = JobRunStepStatus.pending
                });
                if (mainData.NextJobId != null)
                {
                    var _data = await _dbContext.JobTask.FirstOrDefaultAsync(s => s.Id == mainData.NextJobId);
                    if (_data == null) error = $"ID为{mainData.NextJobId}的任务已不存在";
                    listSteps.Add(new JobRunStep
                    {
                        TaskId = mainData.NextJobId,
                        TaskTitle = _data?.Title,
                        Content = _data?.Content,
                        Status = JobRunStepStatus.pending,
                        Result = _data == null ? "[任务不存在]" : ""
                    });
                }
                runData = new JobTaskRun
                {
                    JobId = dataId,
                    JobType = data.JobType,
                    Content = data.Content,
                    Note = $"serverId:{GlobalData.ServerId}",
                    HasNotice = false,
                    RunSteps = listSteps.ToJson(_camelCaseIndented)
                };
                await _dbContext.JobTaskRun.AddAsync(runData);
                await _dbContext.SaveChangesAsync();
            }
            if (error.Has())
            {
                await _updateResult(mainData, false, $"ID为{mainData.PreJobId}的任务已不存在");
                return;
            }
            #endregion

            // 执行前触发任务
            if (isMainTask && mainData.PreJobId != null)
            {
                await _jobService.Once(new RunOnceRequest { Id = Convert.ToInt64(data.PreJobId), MainTaskId = mainTaskId, RunId= runData.Id, IsMainTask= false });
                return;
            }
            
            // run
            listSteps = JsonConvert.DeserializeObject<List<JobRunStep>>(runData.RunSteps);
            var currentStep = listSteps.FirstOrDefault(s => s.TaskId == dataId);
            var nextStep = listSteps.Next(currentStep);
            var currentStepIndex = listSteps.IndexOf(currentStep);
            var preStep = currentStepIndex == 0 ? null : listSteps[currentStepIndex - 1];
            
            try
            {
                var _data = data.DeepCopy();
                
                if (preStep!=null) handlePreParam(_data, preStep);
                switch (data.JobType)
                {
                    case JobType.Url:
                        (isSuccess, result, response) = await Request(_data);
                        break;
                    case JobType.Script:
                        (isSuccess, result, response) = ExecuteScrrpt(_data);
                        break;
                    default:
                        break;
                }
            }
            catch (Exception ex)
            {
                isSuccess = false;
                result = ex.Combine();
            }
            if (listSteps.Count == 1)
            {
                result = result.Omit(490);
            }
            #region 更新结果
            if (nextStep==null) {
                await _updateResult(mainData, isSuccess, result);
            }

            async Task _updateResult(JobTask task,bool isSuccess, string result)
            {
                task.LastExecutionTime = now;
                task.LastExecutionStatus = isSuccess;
                task.Result = result;
                task.ExecutionCount = task.ExecutionCount + 1;
                var executionStatus = task.RecentExecutionStatus + (isSuccess ? "1" : "0");
                task.RecentExecutionStatus = executionStatus.Length > 10 ? executionStatus.Substring(executionStatus.Length - 10) : executionStatus;
                if (task.Cron.Has()) task.NextExecutionTime = SchedulerHelper.NextExecutionTime(task.Cron);
                if (!isSuccess) task.LastFailureTime = now;
                else task.LastSuccessTime = now;
                await _dbContext.SaveChangesAsync();
            }
            #endregion

            #region 更新Run
            currentStep.IsSuccess = isSuccess;
            currentStep.Status = isSuccess?JobRunStepStatus.success:JobRunStepStatus.error;
            currentStep.Content = data.Content;
            currentStep.Result = result;
            currentStep.Response = response;
            currentStep.RunTime = DateTime.Now;
            runData.RunSteps= listSteps.ToJson(_camelCaseIndented);
            if (!isSuccess||listSteps.All(s=>s.Status==JobRunStepStatus.success))
            {
                runData.IsSuccess = isSuccess;
            }
            if (mainTaskId==data.Id)
            {
                runData.Result = result;
            }
            #endregion

            #region 通知策略
            var needNotice = false;
            switch (mainData.NoticePolicy)
            {
                case NoticePolicy.OnlyException:
                    if (!isSuccess) needNotice = true;
                    break;
                case NoticePolicy.ExceptionOrRecovery:
                    var lastRecord = await _dbContext.JobTaskRun.OrderByDescending(s => s.CreateTime).FirstOrDefaultAsync(s => s.JobId == data.Id);
                    var isRecovery = lastRecord.IsSuccess==false && isSuccess;//结果是否为恢复（即上次失败，本次成功）
                    if (!isSuccess || isRecovery) needNotice = true;
                    break;
                case NoticePolicy.Never:
                    needNotice = false;
                    break;
                case NoticePolicy.All:
                    needNotice = true;
                    break;
                default:
                    needNotice = false;
                    break;
            }
            if (needNotice)
            {
                var noticeObjects = (mainData.NoticeObjects ?? "").Replace("；", ";").Replace(",", ";").Replace("，", ";").TrimEnd(';').Split(';').Distinct();//规范统一数据
                if (noticeObjects.Has())
                {
                    var notices = new List<Notice>();
                    foreach (var item in noticeObjects)
                    {
                        if (!item.Has()) continue;
                        var notice = new Notice
                        {
                            Title = mainData.Title,
                            CreateTime = now,
                            IsBusinessSuccess = isSuccess,
                            Content = $"组:{mainData.Group};Url:{mainData.Content};结果:{mainData.Result};".MaxLength(2000),
                            NoticeObject = item,
                            SourceId = runData.Id
                        };
                        notices.Add(notice);
                    }
                    if (notices.Has())
                    {
                        _dbContext.AddRange(notices);
                        runData.HasNotice = true;
                    }
                }
            }
            #endregion

            await _dbContext.SaveChangesAsync();

            // 触发下个任务
            if (isSuccess && nextStep != null)
            {
                var nextJob = await _dbContext.JobTask.FirstOrDefaultAsync(s => s.Id == nextStep.TaskId);
                //nextJob.PreJobParam= result;
                await _jobService.Once(new RunOnceRequest { Id = (long)nextStep.TaskId, MainTaskId = mainTaskId, RunId = runId, IsMainTask = false });
            }
            return;
        }

        private void handlePreParam(JobTask jobTask, JobRunStep preStep)
        {
            // preParam="{'title':'1','b':'2'}"
            // header= access_token:${pre.response.access_token}
            var data = new
            {
                pre = new
                {
                    response = JObject.Parse(preStep.Response)
                }
            };
            // header
            try
            {
                if (jobTask.RequestHeader.Has()) jobTask.RequestHeader = ParseManager.Parse(jobTask.RequestHeader, data);
            }
            catch (Exception e)
            {

            }
            // url
            try
            {
                if (jobTask.Content.Has()) jobTask.Content = ParseManager.Parse(jobTask.Content, data);
            }
            catch (Exception e)
            {

            }
            //body
            try
            {
                if (jobTask.RequestBody.Has()) jobTask.RequestBody = ParseManager.Parse(jobTask.RequestBody, data);
            }
            catch (Exception e)
            {

            }
        }

        private bool IsJsonString(string input)
        {
            try
            {
                // 尝试将字符串解析为JSON对象或数组
                var obj = JToken.Parse(input);
                return true;
            }
            catch (JsonReaderException)
            {
                // 如果解析失败，说明不是有效的JSON字符串
                return false;
            }
        }


        private async Task<(bool isSuccess, string result, string response)> Request(JobTask data)
        {
            var isSuccess = false;
            var result = string.Empty;
            var response = string.Empty;

            try
            {
                var headers = HttpHelper.ConvertToHeaderDic(data.RequestHeader).Where(s => !s.Key.StartsWith(":")).Select(s => (s.Key, s.Value)).ToList();

                var retryWait = new TimeSpan[] { };
                if (data.FailureRetryPolicy.Has())
                {
                    retryWait = data.FailureRetryPolicy.Split(",").Where(s => s.Has()).Select(s => TimeSpan.FromSeconds(Convert.ToInt32(s))).ToArray();
                }
                //随机延迟
                if (!_isRunOnce && data.RandomDelay > 0) await Task.Delay(_random.Next(0, data.RandomDelay * 1000));

                // polly失败重试
                var retryPolicy = Policy
                    .Handle<Exception>()
                    .WaitAndRetryAsync(retryWait, (exception, timeSpan, retryCount, context) =>
                    {
                        var msg = $"错误：{exception.Combine()}，开始重试第{retryCount}次[{DateTime.Now}]..\r\n";
                        result += msg;
                    });
                var res = await retryPolicy.ExecuteAsync(async () =>
                {
                    return await _httpClient.Request(HttpHelper.GetHttpMethod(data.RequestMethond ?? "get"), data.Content, headers, data.RequestBody);
                });
                var responseStr = await res.Content.ReadAsStringAsync();
                response = responseStr;
                if (data.Expected.Has())
                {
                    if (IsEligible(data.Expected, responseStr))
                    {
                        isSuccess = true;
                        result = responseStr;
                    }
                    else
                    {
                        isSuccess = false;
                        result = (new { HttpStatusCode = res.StatusCode, 期望值 = data.Expected, 实际值 = responseStr }).ToJson();
                    }
                }
                else
                {
                    isSuccess = res.IsSuccessStatusCode && res.StatusCode == System.Net.HttpStatusCode.OK;
                    //if (!isSuccess) result = (new { HttpStatusCode = res.StatusCode, 实际值 = responseStr }).ToJson();
                    //result = isSuccess? res.StatusCode.ToString();
                    result = (new { HttpStatusCode = res.StatusCode }).ToJson();
                }
            }
            catch (Exception e)
            {
                result += e.Combine();
            }

            return (isSuccess, result, response);
        }
        // exec Scrrpt
        private (bool isSuccess, string result, string response) ExecuteScrrpt(JobTask data)
        {
            bool isSuccess = false;
            string result;
            string response = string.Empty;
            if (Environment.OSVersion.Platform == PlatformID.Unix)
            {
                var process = new Process();
                process.StartInfo.FileName = "bash"; // 使用bash来执行脚本
                var content = data.Content.Replace("\"", "\\\"");
                process.StartInfo.Arguments = $"-c \"{content}\"";
                process.StartInfo.RedirectStandardOutput = true;
                process.StartInfo.RedirectStandardError = true;
                process.StartInfo.UseShellExecute = false;
                process.StartInfo.CreateNoWindow = true;

                process.Start();

                string output = process.StandardOutput.ReadToEnd();
                output = output.TrimEnd('\r', '\n');
                string error = process.StandardError.ReadToEnd();

                process.WaitForExit();
                int exitCode = process.ExitCode;
                response = output;
                if (data.Expected.Has())
                {
                    if (IsEligible(data.Expected, output))
                    {
                        isSuccess = true;
                        result = output;
                    }
                    else
                    {
                        isSuccess = false;
                        result = (new { ExitCode = exitCode, 期望值 = data.Expected, 实际值 = output, error = error }).ToJson();
                    }
                }
                else
                {
                    isSuccess = exitCode == 0;
                    result = exitCode == 0 ? output : error;
                }
            }
            else if (Environment.OSVersion.Platform == PlatformID.Win32NT)
            {
                Process process = new Process();
                process.StartInfo.FileName = "cmd.exe";
                process.StartInfo.Arguments = "/c " + data.Content;
                process.StartInfo.RedirectStandardOutput = true;
                process.StartInfo.UseShellExecute = false;
                process.StartInfo.CreateNoWindow = true;

                process.Start();
                string output = process.StandardOutput.ReadToEnd();
                output = output.TrimEnd('\r', '\n');
                process.WaitForExit();

                int exitCode = process.ExitCode;
                response = output;
                if (data.Expected.Has())
                {
                    if (IsEligible(data.Expected, output))
                    {
                        isSuccess = true;
                        result = output;
                    }
                    else
                    {
                        isSuccess = false;
                        result = (new { ExitCode = exitCode, 期望值 = data.Expected, 实际值 = output }).ToJson();
                    }
                }
                else
                {
                    isSuccess = exitCode == 0;
                    result = output;
                }
            }
            else
            {
                result = $"::不支持的平台：{Environment.OSVersion.Platform.GetName()}";
            }
            return (isSuccess, result, response);



            //(bool isSuccess, string result) sfd(string expected, string output,string msg)
            //{
            //    if (output == data.Expected || output.IsMatch(data.Expected))
            //    {
            //        isSuccess = true;
            //        result = output;
            //    }
            //    else
            //    {
            //        isSuccess = false;
            //        result = (new { ExitCode = exitCode, 期望值 = expected, 实际值 = output, error = error }).ToJson();
            //    }
            //}

        }
        //符合期望
        bool IsEligible(string expected, string actual)
        {
            return actual == expected || actual.IsMatch($"^{expected}$");
        }
    }

    public class DeleteJobListener : IJobListener
    {
        public string Name => "DeleteJobListener";

        public Task JobExecutionVetoed(IJobExecutionContext context, CancellationToken cancellationToken = default)
        {
            return Task.CompletedTask;
        }

        public Task JobToBeExecuted(IJobExecutionContext context, CancellationToken cancellationToken = default)
        {
            return Task.CompletedTask;
        }

        public async Task JobWasExecuted(IJobExecutionContext context, JobExecutionException jobException, CancellationToken cancellationToken = default)
        {
            IScheduler scheduler = context.Scheduler;
            await scheduler.DeleteJob(context.JobDetail.Key);
        }
    }

}
